#include"StringPool.h"
#include"variable.h"
#include"Parameters.h"
#include<cmath>
#include"Util.h"
#include<iostream>
#include<fstream>
#include<map>
#include"boost/regex.hpp"
using namespace std;
using namespace boost;

Parameters::Parameters(int size){
	if(size == 0){
		parameters = NULL;
		total = NULL;
	}
	else{
		parameters = new double[size];
		total = new double[size];
		memset(parameters, 0, sizeof(double) * size);
		memset(total, 0, sizeof(double) * size);
	}
	string tmp = "punc";
	lossType = pool->get(tmp);
	this->size = size;
}

Parameters::Parameters(int size, double *param){
	string tmp = "punc";
	lossType = pool->get(tmp);
	this->size = size;
	this->parameters = param;
	this->total = NULL;
}

void Parameters::setParameters(double *param){
	this->parameters = param;
}

Parameters::~Parameters(){
	if(parameters != NULL){
		delete[](parameters);
	}
	if(total != NULL){
		delete[](total);
	}
	pool->push_back(lossType);
}

void Parameters::setLoss(std::string &it){
	if(lossType != NULL){
		pool->push_back(lossType);
	}
	lossType = pool->get(it);
}

void Parameters::averageParams(double avVal){
	for(int i = 0; i < size; i++){
		total[i] *= (1.0 / avVal);
	}

	if(parameters != NULL){
		delete[](parameters);
	}
	parameters = total;
	total = NULL;
}

void Parameters::updateParametersMIRA(DependencyInstance *inst, std::vector<pair<FeatureVector*,string*> > &d, double upd){
	string* actParseTree = inst->actParserTree;
	FeatureVector* actFV = inst->fv;
	int K = 0;
	int len = (int)(d.size());
	for(int i = 0; i < len && d[i].first != NULL; i++){
		K = i+1;
	}

	vector<double> b = vector<double>(K);
	vector<double> lam_dist = vector<double>(K);
	vector<FeatureVector*> dist = vector<FeatureVector*>(K);
	
	for(int k = 0;k < K; k++){
		lam_dist[k] = getScore(actFV) - getScore(d[k].first);
		b[k] = numErrors(inst, d[k].second, actParseTree);
		b[k] -= lam_dist[k];
		dist[k] = actFV->getDistVector(d[k].first);
	}
	vector<double>* alpha = hildreth(dist, b);

	FeatureVector* fv = NULL;

	for(int k = 0; k < K; k++){
		fv = dist[k];
		fv->update(parameters, total, (*alpha)[k], upd);
	}
	
	for(int i = 0; i < K; i++){
		delete(dist[i]);
	}
	delete(alpha);
}

void Parameters::updateParemetersAP(DependencyInstance *inst, std::vector<pair<FeatureVector*,string*> > &d, double upd){
	string* actParseTree = inst->actParserTree;
	FeatureVector* actFV = inst->fv;
	int b = (int)(numErrors(inst, d[0].second, actParseTree));
	if(b > 0){
		FeatureVector* dist = actFV->getDistVector(d[0].first);
		dist->update(parameters, total, 1, upd);
		delete(dist);
	}
}

double Parameters::getScore(FeatureVector *fv){
	return fv->getScore(parameters);
}

vector<double>* Parameters::hildreth(std::vector<FeatureVector*> &a, std::vector<double> &b){
	int i;
	int max_iter = 10000;
	double eps = 0.00000001;
	double zero = 0.000000000001;

	vector<double>* alpha = new vector<double>(b.size(), 0.0);
	vector<double> F = vector<double>(b.size());
	vector<double> kkt = vector<double>(b.size());
	double max_kkt = Negative_Infinity;

	int K = (int)(a.size());

	vector<vector<double> > A = vector<vector<double> >(K);
	for(i = 0; i < K; i++){
		A[i] = vector<double>(K);
	}
	vector<bool> is_computed = vector<bool>(K);

	for(i = 0; i < K; i++){
		A[i][i] = a[i]->dotProduct(*(a[i]));
		is_computed[i] = false;
	}

	int max_kkt_i = -1;

	int len = (int)(F.size());
	for(i = 0; i < len; i++){
		F[i] = b[i];
		kkt[i] = F[i];
		if(kkt[i] > max_kkt){
			max_kkt = kkt[i];
			max_kkt_i = i;
		}
	}
	
	int iter = 0;
	double diff_alpha;
	double try_alpha;
	double add_alpha;
	while(max_kkt >= eps && iter < max_iter){
		diff_alpha = (A[max_kkt_i][max_kkt_i] <= zero) ? 0.0 : F[max_kkt_i] / A[max_kkt_i][max_kkt_i];
		try_alpha = (*alpha)[max_kkt_i] + diff_alpha;
		add_alpha = 0.0;

		if(try_alpha < 0.0){
			add_alpha = -1.0 * (*alpha)[max_kkt_i];
		}
		else{
			add_alpha = diff_alpha;
		}

		(*alpha)[max_kkt_i] = (*alpha)[max_kkt_i] + add_alpha;
		if(!is_computed[max_kkt_i]){
			for(i = 0; i < K; i++){
				A[i][max_kkt_i] = a[i]->dotProduct(*(a[max_kkt_i]));
				is_computed[max_kkt_i] = true;
			}
		}

		for(i = 0; i < len; i++){
			F[i] -= add_alpha * A[i][max_kkt_i];
			kkt[i] = F[i];
			if((*alpha)[i] > zero){
				kkt[i] = fabs(F[i]);
			}
		}

		max_kkt = Negative_Infinity;
		max_kkt_i = -1;
		for(i = 0; i < len; i++){
			if(kkt[i] > max_kkt){
				max_kkt = kkt[i];
				max_kkt_i = i;
			}
		}
		iter++;
	}
	return alpha;
}

double Parameters::numErrors(DependencyInstance *inst, std::string *pred, std::string *act){
	if(*lossType == "nopunc"){
		return numErrorsDepNoPunc(inst, pred, act) + numErrorsLabelNoPunc(inst, pred, act);
	}
	else{
		return numErrorsDep(pred, act) + numErrorsLabel(pred, act);
	}
};

double Parameters::numErrorsDep(std::string *pred, std::string *act){
	vector<string*>* act_spans = Util::split(act, ' ');
	vector<string*>* pred_spans = Util::split(pred, ' ');

	int correct = 0;
	
	int len = (int)(pred_spans->size());
	for(int i = 0; i < len; ++i){
		int first_pos = (int)((*pred_spans)[i]->find_first_of(':'));
		string p = (*pred_spans)[i]->substr(0, first_pos);
		first_pos = (int)((*act_spans)[i]->find_first_of(':'));
		string a = (*act_spans)[i]->substr(0, first_pos);
		if(p == a){
			correct ++;
		}
	}
	int length = (int)(act_spans->size());
	for(int i = 0; i < len; ++i){
		pool->push_back((*pred_spans)[i]);
	}
	delete(pred_spans);

	for(int i = 0; i < length; i++){
		pool->push_back((*act_spans)[i]);
	}
	delete(act_spans);

	return ((double)length) -correct;
}

double Parameters::numErrorsLabel(std::string *pred, std::string *act){
	vector<string*>* act_spans = Util::split(act, ' ');
	vector<string*>* pred_spans = Util::split(pred, ' ');

	int correct = 0;

	int len = (int)(pred_spans->size());
	for(int i = 0; i < len; ++i){
		int first_pos = (int)((*pred_spans)[i]->find_first_of(':'));
		int length = (int)((*pred_spans)[i]->length());
		string p = (*pred_spans)[i]->substr(first_pos + 1, length - first_pos -1);
		first_pos = (int)((*act_spans)[i]->find_first_of(':'));
		length = (int)((*act_spans)[i]->length());
		string a = (*act_spans)[i]->substr(first_pos + 1, length - first_pos -1);
		if(p == a){
			correct++;
		}
	}

	int length = (int)(act_spans->size());
	vector<string*>::iterator iter;
	for(int i = 0; i < len; i++){
		pool->push_back((*pred_spans)[i]);
	}
	delete(pred_spans);

	for(int i = 0; i < length; i++){
		pool->push_back((*act_spans)[i]);
	}
	delete(act_spans);
	return ((double)length) -correct;
}

double Parameters::numErrorsDepNoPunc(DependencyInstance *inst, std::string *pred, std::string *act){
	vector<string*>* act_spans = Util::split(act, ' ');
	vector<string*>* pred_spans = Util::split(pred, ' ');
	vector<string*>* pos = inst->postags;

	int correct = 0;
	int numPunc = 0;
	
	for(int i = 0; i < (int)(pred_spans->size()); ++i){
		string::size_type first_pos = (*pred_spans)[i]->find_first_of(':');
		string p = (*pred_spans)[i]->substr(0, first_pos);
		first_pos = (*act_spans)[i]->find_first_of(':');
		string a = (*act_spans)[i]->substr(0, first_pos);
		if(punctSet.count(*((*pos)[i+1])) > 0){
			numPunc++;
			continue;
		}
		if(p == a){
			correct++;
		}
	}
	int length = (int)(act_spans->size());
	vector<string*>::iterator iter;
	for(iter = pred_spans->begin(); iter != pred_spans->end(); ++iter){
		pool->push_back(*iter);
	}
	delete(pred_spans);

	for(iter = act_spans->begin(); iter != act_spans->end(); ++iter){
		pool->push_back(*iter);
	}
	delete(act_spans);
	return ((double)length) -correct - numPunc;
}

double Parameters::numErrorsLabelNoPunc(DependencyInstance *inst, std::string *pred, std::string *act){
	vector<string*>* act_spans = Util::split(act, ' ');
	vector<string*>* pred_spans = Util::split(pred, ' ');
	vector<string*>* pos = inst->postags;

	int correct = 0;
	int numPunc = 0;
	
	for(int i = 0; i < (int)(pred_spans->size()); ++i){
		string::size_type first_pos = (*pred_spans)[i]->find_first_of(':');
		string::size_type second_pos = (*pred_spans)[i]->find(':', first_pos + 1);
		string p = (*pred_spans)[i]->substr(first_pos + 1, second_pos - first_pos -1);
		first_pos = (*act_spans)[i]->find_first_of(':');
		second_pos = (*act_spans)[i]->find(':', first_pos + 1);
		string a = (*act_spans)[i]->substr(first_pos + 1, second_pos - first_pos -1);
		if(punctSet.count(*((*pos)[i+1])) > 0){
			numPunc++;
			continue;
		}
		if(p == a){
			correct++;
		}
	}
	int length = (int)(act_spans->size());
	vector<string*>::iterator iter;
	for(iter = pred_spans->begin(); iter != pred_spans->end(); ++iter){
		pool->push_back(*iter);
	}
	delete(pred_spans);

	for(iter = act_spans->begin(); iter != act_spans->end(); ++iter){
		pool->push_back(*iter);
	}
	delete(act_spans);
	return ((double)length) -correct - numPunc;
}

